grammar SerieParallel;

options {
  language = Java;
  // generated parser should create abstract syntax tree
  //output = AST;
}

@lexer::header {
  package lifo.GammaTaf.SerieParallel.parsers;  
}

//add new members to generated lexer
@lexer::members {
  //add new field
  private List<RecognitionException> errors = new ArrayList <RecognitionException> ();
   
  //add new method
  public List<RecognitionException> getAllErrors() {
    return new ArrayList<RecognitionException>(errors);
  }
 
  //add new method
  public boolean hasErrors() {
    return !errors.isEmpty();
  }
  
   public void reportError(RecognitionException e) {
    errors.add(e);
    displayRecognitionError(this.getTokenNames(), e);
  }
} 

@parser::header {
  package lifo.GammaTaf.SerieParallel.parsers;
  import lifo.GammaTaf.SerieParallel.SerieParallel;
}
@parser::members {
  SerieParallel res;
  
  public static SerieParallel parserString(String s) throws Exception {
    CharStream input = new ANTLRStringStream(s);
    return parser(input);       
  }

  public static SerieParallel parserFile(String s) throws Exception {
    CharStream input = new ANTLRFileStream(s);
    return parser(input);       
  }
  
  public static SerieParallel parser(CharStream input) throws Exception {
    SerieParallelLexer lexer = new SerieParallelLexer(input);
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    SerieParallelParser parser = new SerieParallelParser(tokens);
    parser.rule();
       
    return parser.res;
  }

  //add new field
  private List<RecognitionException> errors = new ArrayList <RecognitionException> ();
   
  //add new method
  public List<RecognitionException> getAllErrors() {
    return new ArrayList<RecognitionException>(errors);
  }
 
  //add new method
  public boolean hasErrors() {
    return !errors.isEmpty();
  }
  
  public void reportError(RecognitionException e) {
    errors.add(e);
    displayRecognitionError(this.getTokenNames(), e);
  }
    
}

// parser rules
rule : expression
  {
    res = $expression.b;
  }
  ;

expression returns [SerieParallel b]
  : s0=serie 
    {
    $b = $s0.b;
    ArrayList<SerieParallel> arg = new ArrayList<SerieParallel>();
    arg.add($b);
  }
  ('+' s1=serie
  {
    arg.add($s1.b);
  }
  )*
  {
    if (arg.size() > 1)
      $b = new SerieParallel.Sum(arg.toArray(new SerieParallel[]{}));
  };

serie returns [SerieParallel b]
  : p0=parallel 
  {
    $b = $p0.b;
    ArrayList<SerieParallel> arg = new ArrayList<SerieParallel>();
    arg.add($b);
  }
  ('.' p1=parallel
   {
    arg.add($p1.b);
  }  
  )*
    {
    if (arg.size() > 1)
      $b = new SerieParallel.Serie(arg.toArray(new SerieParallel[]{}));
  }
  ;

parallel returns [SerieParallel b]
  : s0=star 
  {
    $b = $s0.b;
    ArrayList<SerieParallel> arg = new ArrayList<SerieParallel>();
    arg.add($b);
  }
  ('||' s1=star
   {
    arg.add($s1.b);
  }
  )*
  {
      if (arg.size() > 1)
      $b = new SerieParallel.Parallel(arg.toArray(new SerieParallel[]{}));
  }
  ;

star returns [SerieParallel b]
  : atom 
  {
    $b = $atom.b;
  }
  (STAR
  {
    $b = new SerieParallel.Star($b);
  }
  |
  SHARP
  {
    $b = new SerieParallel.Sharp($b);
  }
  )?;

atom returns [SerieParallel b]
  : ID 
  {
    $b = new SerieParallel.Action($ID.text);
  }
  | '(' expression ')'
  {
    $b = $expression.b;
  };

//lexer rules
ID  : ('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
STAR : '*';
SHARP : '#';
//Ignore white spaces
WS : ( ' ' | '\t' | '\r' | '\n' )+ { $channel = HIDDEN; };